package io.gitee.zhangbinhub.acp.cloud.gateway.filter

import io.gitee.zhangbinhub.acp.boot.conf.AcpCorsConfiguration
import io.gitee.zhangbinhub.acp.boot.constant.AcpCoreConstant
import io.gitee.zhangbinhub.acp.boot.exceptions.WebException
import io.gitee.zhangbinhub.acp.boot.http.HttpHeaders
import io.gitee.zhangbinhub.acp.boot.http.HttpStatus
import io.gitee.zhangbinhub.acp.boot.log.LogAdapter
import io.gitee.zhangbinhub.acp.core.common.CommonTools
import org.noear.solon.annotation.Component
import org.noear.solon.cloud.gateway.CloudGatewayFilter
import org.noear.solon.cloud.gateway.exchange.ExContext
import org.noear.solon.cloud.gateway.exchange.ExFilterChain
import org.noear.solon.rx.Completable
import org.noear.solon.web.cors.CrossHandler


@Component(index = AcpCoreConstant.CORS_FILTER_ORDER)
class CloudGatewayCorsFilter(
    private val logAdapter: LogAdapter,
    private val acpCorsConfiguration: AcpCorsConfiguration
) : CloudGatewayFilter {
    private val crossHandler = CrossHandler().apply {
        this.allowedOrigins(acpCorsConfiguration.allowedOrigins.joinToString(separator = ","))
        if (acpCorsConfiguration.allowedMethods.isEmpty()) {
            this.allowedMethods("*")
        } else {
            this.allowedMethods(acpCorsConfiguration.allowedMethods.joinToString(separator = ","))
        }
        if (acpCorsConfiguration.allowedHeaders.isEmpty()) {
            this.allowedHeaders("*")
        } else {
            this.allowedHeaders(acpCorsConfiguration.allowedHeaders.joinToString(separator = ","))
        }
        if (acpCorsConfiguration.exposedHeaders.isEmpty()) {
            this.exposedHeaders("*")
        } else {
            this.exposedHeaders(acpCorsConfiguration.exposedHeaders.joinToString(separator = ","))
        }
        this.allowCredentials(acpCorsConfiguration.allowCredentials)
        this.maxAge(acpCorsConfiguration.maxAge)
    }

    override fun doFilter(exContext: ExContext, chain: ExFilterChain): Completable = try {
        val ctx = exContext.toContext()
        val origin = ctx.header(HttpHeaders.ORIGIN)
        if (!CommonTools.isNullStr(origin)) {
            crossHandler.handle(ctx)
            if (!CommonTools.isNullStr(ctx.headerOfResponse(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN))) {
                if (!ctx.handled) {
                    chain.doFilter(exContext)
                } else {
                    Completable.complete()
                }
            } else {
                throw WebException(HttpStatus.FORBIDDEN, "Invalid CORS request")
            }
        } else {
            chain.doFilter(exContext)
        }
    } catch (e: Throwable) {
        logAdapter.error(e.message, e)
        throw e
    }
}